package tls

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"os"
)

func TLSConfigFor(c *TLSClientConfig) (*tls.Config, error) {
	if c.Insecure || !c.HasCertAuth() || len(c.ServerName) == 0 {
		return &tls.Config{InsecureSkipVerify: true}, nil
	}

	// 从文件读取 ca 证书、客户端证书、秘钥
	if err := LoadTLSFiles(c); err != nil {
		return nil, err
	}

	// tls 配置结构体
	tlsConfig := &tls.Config{
		// Can't use SSLv3 because of POODLE and BEAST
		// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
		// Can't use TLSv1.1 because of RC4 cipher usage
		MinVersion: tls.VersionTLS12,
		//nolint: gosec
		InsecureSkipVerify: c.Insecure,
		ServerName:         c.ServerName,
		NextProtos:         c.NextProtos,
	}

	// 添加服务端根证书
	if c.HasCA() {
		tlsConfig.RootCAs = rootCertPool(c.CAData)
	}

	var staticCert *tls.Certificate

	// 校验客户证书和秘钥
	cert, err := tls.X509KeyPair(c.CertData, c.KeyData)
	if err != nil {
		return nil, err
	} else {
		staticCert = &cert
	}

	// 设置向服务器递交客户证书的方法
	tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
		if staticCert != nil {
			return staticCert, nil
		}

		return &tls.Certificate{}, nil
	}

	return tlsConfig, nil
}

func LoadTLSFiles(c *TLSClientConfig) error {
	var err error

	c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
	if err != nil {
		return err
	}

	c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
	if err != nil {
		return err
	}

	c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
	if err != nil {
		return err
	}

	return nil
}

func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
	if len(data) > 0 {
		return base64.StdEncoding.DecodeString(string(data))
	}

	if len(file) > 0 {
		fileData, err := os.ReadFile(file)
		if err != nil {
			return []byte{}, err
		}
		return fileData, nil
	}

	return nil, nil
}

func rootCertPool(caData []byte) *x509.CertPool {
	if len(caData) == 0 {
		return nil
	}

	certPool := x509.NewCertPool()
	certPool.AppendCertsFromPEM(caData)

	return certPool
}
